#include "stdio.h"
#include "stdint.h"
#include "string.h"
#include "float.h"
#include "math.h"
#include "cJSON.h"
#include "kfifo.h"
// #include "DevicesCRC.h"
#include "FeedPhy.h"
#include "FeedFrame.h"
#include "FeedJson.h"
#include "FeedPack.h"


FeedPackType g_typeFeedPackRead;
FeedPackType g_typeFeedPackSend;


int8_t cFeedPackFrameSend(FeedPackType *ptypePack);
int8_t cFeedPackJsonSend(FeedPackType *ptypePack);


static uint16_t usCRC16_MODBUS(uint16_t *pusInitCRC, void *pvBuff, int32_t iLength)
{
    uint8_t *pucDataBuff = pvBuff;
    uint16_t usInputCRC;
    int8_t i;

    if((pvBuff == NULL) || (iLength < 1))
        return 0;

    usInputCRC = (pusInitCRC != NULL) ? *pusInitCRC : 0xFFFF;

    while((iLength--) > 0)
    {
        usInputCRC ^= *pucDataBuff++;

        for(i = 0; i < 8; ++i)
        {
            if (usInputCRC & 1)
                usInputCRC = (usInputCRC >> 1) ^ 0xA001;
            else
                usInputCRC >>= 1;
        }
    }

    return usInputCRC;
}

int8_t cFeedPackEncode(FeedPackType *ptypePack)
{
    static uint32_t st_uiPackNo = 0;
    uint16_t usCrc = 0;

    if(ptypePack == NULL)
        return 1;

    ptypePack->head[0] = 0x55;
    ptypePack->head[1] = 0xAA;
    ptypePack->version = 0;
    ptypePack->No = st_uiPackNo++;

    usCrc = usCRC16_MODBUS((uint16_t *)NULL, ptypePack, (int32_t)ptypePack->length + 8);

    ptypePack->datas[ptypePack->length]     = usCrc;
    ptypePack->datas[ptypePack->length + 1] = usCrc >> 8;

    return 0;
}

FeedPackStateEnum enumFeedPackDecode(FeedPackType *ptypePack, uint8_t ucByte)
{
    if(ptypePack == NULL)
        return 1;

    if(ptypePack->state == FEED_PACK_STATE_END)
    {
        ptypePack->state = FEED_PACK_STATE_HEAD0;
    }

    switch(ptypePack->state)
    {
        case FEED_PACK_STATE_HEAD0:
            ptypePack->head[0] = ucByte;
            ptypePack->state = (ucByte == 0x55) ? FEED_PACK_STATE_HEAD1 : FEED_PACK_STATE_HEAD0;
        break;

        case FEED_PACK_STATE_HEAD1:
            ptypePack->head[1] = ucByte;
            ptypePack->state = (ucByte == 0xAA) ? FEED_PACK_STATE_VERSION : ((ucByte == 0x55) ? FEED_PACK_STATE_HEAD1 : FEED_PACK_STATE_HEAD0);
        break;

        case FEED_PACK_STATE_VERSION:
            ptypePack->version = ucByte;
            ptypePack->state = FEED_PACK_STATE_NO;
        break;

        case FEED_PACK_STATE_NO:
            ptypePack->No = ucByte;
            ptypePack->state = FEED_PACK_STATE_CHANNEL;
        break;

        case FEED_PACK_STATE_CHANNEL:
            ptypePack->target = ucByte >> 4;
            ptypePack->source = ucByte & 0x0F;
            ptypePack->state = FEED_PACK_STATE_TYPE;
        break;

        case FEED_PACK_STATE_TYPE:
            ptypePack->get      = (ucByte >> 0) & 1;
            ptypePack->response = (ucByte >> 1) & 1;
            ptypePack->state = FEED_PACK_STATE_LENGTH_LOW;
        break;

        case FEED_PACK_STATE_LENGTH_LOW:
            ptypePack->length = ucByte;
            ptypePack->state = FEED_PACK_STATE_LENGTH_HIGH;
        break;

        case FEED_PACK_STATE_LENGTH_HIGH:
            ptypePack->length |= ((uint16_t)ucByte << 8);
            ptypePack->state = (ptypePack->length == 0) ? FEED_PACK_STATE_CRC_LOW : ((ptypePack->length <= sizeof(ptypePack->datas)) ? FEED_PACK_STATE_DATAS : FEED_PACK_STATE_HEAD0);
            ptypePack->cnt = 0;
        break;

        case FEED_PACK_STATE_DATAS:
            ptypePack->datas[ptypePack->cnt++] = ucByte;
            ptypePack->state = (ptypePack->cnt < ptypePack->length) ? FEED_PACK_STATE_DATAS : FEED_PACK_STATE_CRC_LOW;
        break;

        case FEED_PACK_STATE_CRC_LOW:
            ptypePack->crc = ucByte;
            ptypePack->state = FEED_PACK_STATE_CRC_HIGH;
        break;

        case FEED_PACK_STATE_CRC_HIGH:
            ptypePack->crc |= ((uint16_t)ucByte << 8);
            ptypePack->state = (ptypePack->crc == usCRC16_MODBUS(NULL, ptypePack, ptypePack->length + 8)) ? FEED_PACK_STATE_END : FEED_PACK_STATE_HEAD0;
        break;

        default: break;
    }

    return ptypePack->state;
}

int8_t cFeedPackAnalysis(uint32_t uiChannel, void *pvDatas, int32_t iLength)
{
    FeedPackType *ptypePack = &g_typeFeedPackRead;
    uint8_t *pucDatas = pvDatas, ucAddr;

    /* 与设备通信，以TLV格式 */
    if(uiChannel == FEED_ADDR_DEVICE)
    {
        while((iLength--) > 0)
        {
            if(enumFeedPackDecode(ptypePack, *pucDatas++) == FEED_PACK_STATE_END)
            {
                /* 清空缓存 */
                kfifo_reset(&g_typekFIFOFrame);

                /* 模组（解析并回复） */
                if(ptypePack->target == FEED_ADDR_IOT)
                {
                    /* 解析处理 */
                    cFeedFrameDecode(((uint8_t *)ptypePack)[5], ptypePack->datas, ptypePack->length);

                    /* 收到的是响应，不再循环回复 */
                    if(ptypePack->response == 0)
                    {
                        ucAddr = ptypePack->source;
                        ptypePack->source = ptypePack->target;
                        ptypePack->target = ucAddr;
                        ptypePack->response = 1;
                        cFeedPackFrameSend(ptypePack);
                    }
                }
                /* 其它（转换并发送） */
                else
                {
                    /* 转换成 JSON 格式，再转发给服务器、APP */
                    kfifo_in(&g_typekFIFOFrame, ptypePack->datas, ptypePack->length);

                    cFeedPackJsonSend(ptypePack);
                }
            }
        }
    }
    /* 与其它端口通信，以JSON格式 */
    else
    {
        /* 清空缓存 */
        kfifo_reset(&g_typekFIFOFrame);

        /* 解析处理 */
        if((cFeedJsonDecode(ptypePack, pvDatas, iLength)) == 0)
        {
            /* 模组 */
            if(ptypePack->target == FEED_ADDR_IOT)
            {
                /* 收到的是响应，不再循环回复 */
                if(ptypePack->response == 0)
                {
                    ucAddr = ptypePack->source;
                    ptypePack->source = ptypePack->target;
                    ptypePack->target = ucAddr;
                    ptypePack->response = 1;

                    cFeedPackJsonSend(ptypePack);
                }
            }
            /* 其它（转换、发送） */
            else
            {
                cFeedPackFrameSend(ptypePack);
            }
        }
    }

    return 0;
}

int8_t cFeedPackFrameSend(FeedPackType *ptypePack)
{
    FeedFrameType typeFrame;
    int32_t iLength;

    ptypePack->length = 0;

    /* 回复数据 */
    while(kfifo_len(&g_typekFIFOFrame) >= 2)
    {
        /* 获取 */
        if((ptypePack->get == 1) && (ptypePack->response == 0))
        {
            kfifo_out(&g_typekFIFOFrame, &typeFrame, 2);
            iLength = 2;
        }
        /* 获取响应、设置、设置响应 */
        else
        {
            kfifo_out(&g_typekFIFOFrame, &typeFrame, 4);
            iLength = typeFrame.length + 4;

            /* 数据长度错误 */
            if(typeFrame.length > sizeof(typeFrame.datas))
                return 1;

            kfifo_out(&g_typekFIFOFrame, typeFrame.datas, typeFrame.length);
        }

        /* 缓存已满 */
        if(iLength > (sizeof(ptypePack->datas) - ptypePack->length))
        {
            cFeedPackEncode(ptypePack);
            iFeedPhyDatasSend(ptypePack->target, ptypePack, ptypePack->length + 10);

            ptypePack->length = 0;
        }

        memcpy(&ptypePack->datas[ptypePack->length], &typeFrame, iLength);

        ptypePack->length += iLength;
    }

    if(ptypePack->length > 0)
    {
        cFeedPackEncode(ptypePack);
        iFeedPhyDatasSend(ptypePack->target, ptypePack, ptypePack->length + 10);
    }

    return 0;
}

int8_t cFeedPackJsonSend(FeedPackType *ptypePack)
{
    cJSON *pjsonRoot, *pjsonChannel, *pjsonResponse, *pjsonResponseType, *pjsonOut;
    char *pcPrints;
    FeedFrameType typeFrame;
    char cID[8] = {0};

    pjsonRoot           = cJSON_CreateObject();
    pjsonChannel        = cJSON_CreateObject();
    pjsonResponse       = cJSON_CreateObject();
    pjsonResponseType = ((ptypePack->get == 1) && (ptypePack->response == 0)) ? cJSON_CreateArray() : cJSON_CreateObject();
    if((pjsonRoot == NULL) || (pjsonChannel == NULL) || (pjsonResponse == NULL) || (pjsonResponseType == NULL))
    {
        cJSON_Delete(pjsonRoot);
        cJSON_Delete(pjsonChannel);
        cJSON_Delete(pjsonResponse);
        cJSON_Delete(pjsonResponseType);
        return 1;
    }

    /* 转换数据 */
    while(kfifo_len(&g_typekFIFOFrame) >= 2)
    {
        /* 获取 */
        if((ptypePack->get == 1) && (ptypePack->response == 0))
        {
            /* 只获取头部信息 */
            kfifo_out(&g_typekFIFOFrame, &typeFrame, 2);
            typeFrame.length = 0;
        }
        /* 获取响应、设置、设置响应 */
        else
        {
            /* 先获取头部信息 */
            kfifo_out(&g_typekFIFOFrame, &typeFrame, 4);

            if(typeFrame.length > sizeof(typeFrame.datas))
                break;

            /* 先获取数据 */
            kfifo_out(&g_typekFIFOFrame, typeFrame.datas, typeFrame.length);
            typeFrame.datas[typeFrame.length] = 0;
        }

        /* 转换成JSON格式 */
        if(iFeedFrameToJson(&pjsonOut, &typeFrame) == 0)
        {
            /* 获取 */
            if(typeFrame.length == 0)
            {
                if(cJSON_AddItemToArray(pjsonResponseType, pjsonOut) == 0)
                {
                    cJSON_Delete(pjsonOut);
                }
            }
            /* 获取响应、设置、设置响应 */
            else
            {
                snprintf(cID, sizeof(cID), "%d", typeFrame.ID);
                if(cJSON_AddItemToObject(pjsonResponseType, cID, pjsonOut) == 0)
                {
                    cJSON_Delete(pjsonOut);
                }
            }
        }
    }

    /* Type */
    if(pjsonResponseType->child != NULL)
    {
        if(ptypePack->get == 0)
            cJSON_AddItemToObjectCS(pjsonResponse, (ptypePack->response == 0) ? "set" : "set_res", pjsonResponseType);
        else
            cJSON_AddItemToObjectCS(pjsonResponse, (ptypePack->response == 0) ? "get" : "get_res", pjsonResponseType);
    }
    else
        cJSON_Delete(pjsonResponseType);

    /* ID */
    if(pjsonResponse->child != NULL)
        cJSON_AddItemToObjectCS(pjsonChannel, "ID", pjsonResponse);
    else
        cJSON_Delete(pjsonResponse);

    /* 通道 */
    if(pjsonChannel->child != NULL)
    {
        snprintf(cID, sizeof(cID), "%d:%d", ptypePack->target, ptypePack->source);
        cJSON_AddItemToObjectCS(pjsonRoot, cID, pjsonChannel);
    }
    else
        cJSON_Delete(pjsonChannel);

    if(pjsonRoot->child != NULL)
    {
        if((pcPrints = cJSON_PrintUnformatted(pjsonRoot)) != NULL)
        {
            iFeedPhyDatasSend(ptypePack->target, pcPrints, strlen(pcPrints));
            /* 结束符：\r */
            iFeedPhyDatasSend(ptypePack->target, "\r", 1);

            cJSON_free(pcPrints);
        }
    }

    cJSON_Delete(pjsonRoot);

    return 0;
}

/* securely comparison of double-point variables */
static int8_t compare_double(double a, double b)
{
    double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b);
    return (fabs(a - b) <= maxVal * DBL_EPSILON);
}

int8_t cFeedPackFrameSinglePack(FeedPackType *ptypePack, uint8_t ucTarget, uint8_t ucSource, uint16_t usID, uint8_t ucType, int32_t iValue, float fValue, double dValue, void *pvDatas, int32_t iLength)
{
    uint8_t *pucDatas = &ptypePack->datas[4];

    ptypePack->target = ucTarget;
    ptypePack->source = ucSource;

    switch(ucType)
    {
        case FEED_FRAME_TYPE_INT :
            for(iLength = 0; ((iValue != 0) || (iLength == 0)); iValue >>= 8, ++iLength)
            {
                pucDatas[iLength] = iValue;
            }
            break;

        case FEED_FRAME_TYPE_FLOAT :
            if(compare_double(fValue, dValue) != 0)
            {
                iLength = 4;
                memcpy(pucDatas, &fValue, 4);
            }
            else
            {
                iLength = 8;
                memcpy(pucDatas, &dValue, 8);
            }
            break;

        case FEED_FRAME_TYPE_STRING :
            memcpy(pucDatas, pvDatas, iLength);
            break;

        case FEED_FRAME_TYPE_RAW :
            iLength = 0;
            break;

        case FEED_FRAME_TYPE_NULL :
            break;

        default : return 1;
    }

    ptypePack->datas[0] = usID;
    ptypePack->datas[1] = usID >> 8;

    if(ucType == FEED_FRAME_TYPE_NULL)
    {
        ptypePack->length = 2;
    }
    else
    {
        ptypePack->datas[2] = iLength;
        ptypePack->datas[3] = (iLength >> 8) | (ucType << 4);
        ptypePack->length = iLength + 4;
    }

    return 0;
}

int8_t cFeedPackFrameSingleUpdate(uint8_t ucTarget, uint8_t ucSource, uint16_t usID, uint8_t ucType, int32_t iValue, float fValue, double dValue, void *pvDatas, int32_t iLength)
{
    FeedPackType *ptypePack = &g_typeFeedPackSend;

    cFeedPackFrameSinglePack(ptypePack, ucTarget, ucSource, usID, ucType, iValue, fValue, dValue, pvDatas, iLength);

    ptypePack->get = 1;
    ptypePack->response = 1;

    cFeedPackEncode(ptypePack);
    iFeedPhyDatasSend(ptypePack->target, ptypePack, ptypePack->length + 10);

    return 0;
}

int8_t cFeedPackFrameSingleSet(uint8_t ucTarget, uint8_t ucSource, uint16_t usID, uint8_t ucType, int32_t iValue, float fValue, double dValue, void *pvDatas, int32_t iLength)
{
    FeedPackType *ptypePack = &g_typeFeedPackSend;

    cFeedPackFrameSinglePack(ptypePack, ucTarget, ucSource, usID, ucType, iValue, fValue, dValue, pvDatas, iLength);

    ptypePack->get = 0;
    ptypePack->response = 0;

    cFeedPackEncode(ptypePack);
    iFeedPhyDatasSend(ptypePack->target, ptypePack, ptypePack->length + 10);

    return 0;
}

int8_t cFeedPackFrameSingleGet(uint8_t ucTarget, uint8_t ucSource, uint16_t usID)
{
    FeedPackType *ptypePack = &g_typeFeedPackSend;

    cFeedPackFrameSinglePack(ptypePack, ucTarget, ucSource, usID, FEED_FRAME_TYPE_NULL, 0, 0.0f, 0.0, NULL, 0);

    ptypePack->get = 1;
    ptypePack->response = 0;

    cFeedPackEncode(ptypePack);
    iFeedPhyDatasSend(ptypePack->target, ptypePack, ptypePack->length + 10);

    return 0;
}

int8_t cFeedPackJsonSingleUpdate(uint8_t ucTarget, uint8_t ucSource, uint16_t usID, uint8_t ucType, int32_t iValue, float fValue, double dValue, void *pvDatas, int32_t iLength)
{
    char cJsonPrint[64] = {0};

    sprintf(cJsonPrint, "{\"%d:%d\":{\"ID\":{\"get_res\":{\"%d\":", ucTarget, ucSource, usID);
    iFeedPhyDatasSend(ucTarget, cJsonPrint, strlen(cJsonPrint));

    switch (ucType)
    {
        case FEED_FRAME_TYPE_INT:
            sprintf(cJsonPrint, "%d", (int)iValue);
            iFeedPhyDatasSend(ucTarget, cJsonPrint, strlen(cJsonPrint));
            break;

        case FEED_FRAME_TYPE_FLOAT:
            snprintf(cJsonPrint, sizeof(cJsonPrint), "%1.15g", dValue);
            iFeedPhyDatasSend(ucTarget, cJsonPrint, strlen(cJsonPrint));
            break;

        case FEED_FRAME_TYPE_STRING:
            iFeedPhyDatasSend(ucTarget, pvDatas, iLength);
            break;

        case FEED_FRAME_TYPE_RAW:
            iFeedPhyDatasSend(ucTarget, pvDatas, iLength);
            break;

        default: break;
    }

    iFeedPhyDatasSend(ucTarget, "}}}}\r", strlen("}}}}\r"));

    return 0;
}

int8_t cFeedPackJsonSingleSet(uint8_t ucTarget, uint8_t ucSource, uint16_t usID, uint8_t ucType, int32_t iValue, float fValue, double dValue, void *pvDatas, int32_t iLength)
{
    char cJsonPrint[64] = {0};

    sprintf(cJsonPrint, "{\"%d:%d\":{\"ID\":{\"set\":{\"%d\":", ucTarget, ucSource, usID);
    iFeedPhyDatasSend(ucTarget, cJsonPrint, strlen(cJsonPrint));

    switch (ucType)
    {
        case FEED_FRAME_TYPE_INT:
            sprintf(cJsonPrint, "%d", (int)iValue);
            iFeedPhyDatasSend(ucTarget, cJsonPrint, strlen(cJsonPrint));
            break;

        case FEED_FRAME_TYPE_FLOAT:
            snprintf(cJsonPrint, sizeof(cJsonPrint), "%1.15g", dValue);
            iFeedPhyDatasSend(ucTarget, cJsonPrint, strlen(cJsonPrint));
            break;

        case FEED_FRAME_TYPE_STRING:
            iFeedPhyDatasSend(ucTarget, pvDatas, iLength);
            break;

        case FEED_FRAME_TYPE_RAW:
            iFeedPhyDatasSend(ucTarget, pvDatas, iLength);
            break;

        default: break;
    }

    iFeedPhyDatasSend(ucTarget, "}}}}\r", strlen("}}}}\r"));

    return 0;
}

int8_t cFeedPackJsonSingleGet(uint8_t ucTarget, uint8_t ucSource, uint16_t usID)
{
    char cJsonPrint[64] = {0};

    sprintf(cJsonPrint, "{\"%d:%d\":{\"ID\":{\"get\":[\"%d\"]}}}\r", ucTarget, ucSource, usID);

    iFeedPhyDatasSend(ucTarget, cJsonPrint, strlen(cJsonPrint));

    return 0;
}
